/** @jsxImportSource @emotion/react */
import {
    autoUpdate,
    flip,
    FloatingFocusManager,
    FloatingList,
    FloatingNode,
    FloatingPortal,
    FloatingTree,
    offset,
    safePolygon,
    shift,
    useClick,
    useDismiss,
    useFloating,
    useFloatingNodeId,
    useFloatingParentNodeId,
    useFloatingTree,
    useHover,
    useInteractions,
    useListItem,
    useListNavigation,
    useMergeRefs,
    useRole,
    useTypeahead
} from "@floating-ui/react";
import * as React from "react";
// import {Children, cloneElement, forwardRef,} from "react";
import {jsx,css} from "@emotion/react";
import {useTheme} from "./Theme/Providers";
import {MouseEventHandler} from "react";
import {MenuItem as MenuListItem} from "./Menu";


/*直接改造 源自官方例子的： https://codesandbox.io/s/admiring-lamport-5wt3yg?file=/src/DropdownMenu.tsx
* */

const MenuContext = React.createContext<{
    getItemProps: (
        userProps?: React.HTMLProps<HTMLElement>
    ) => Record<string, unknown>;
    activeIndex: number | null;
    setActiveIndex: React.Dispatch<React.SetStateAction<number | null>>;
    setHasFocusInside: React.Dispatch<React.SetStateAction<boolean>>;
    isOpen: boolean;
}>({
    getItemProps: () => ({}),
    activeIndex: null,
    setActiveIndex: () => {},
    setHasFocusInside: () => {},
    isOpen: false
});


interface MenuItemProps {
    label: string;
    disabled?: boolean;
    onClick?: MouseEventHandler | undefined;
    children?: React.ReactNode;
    onPress?: MouseEventHandler | undefined;     //可兼容性
    contentAfter?: React.ReactNode;
    contentBefore?: React.ReactNode;      //前置的图标按钮<MenuItem透传参数;
}
//替换原本MenuItem
/** 叶子节点的菜单项目； 必须放在DdMenu下面的；
 注意这样的 <DdMenu label="菜单"  icon={<IconMoreVertical />}>
 <React.Fragment>
 <DdMenuItem label="Undo" />
 【特别注意】多套一层<></>就无法正常工作的。
 # 若有children儿子的就显示为儿子。否则就按照label显示的。 两个情况的样式控制不同。
 实际上：forwardRef<T, P = {}> 在P模型中定义onClick是关键触发！
 * */
//原本类型 MenuItemProps & React.ButtonHTMLAttributes<HTMLButtonElement>
export const DdMenuItem =
    ({ label, disabled,children, ref, ...props }: (MenuItemProps & React.HTMLProps<HTMLButtonElement>) ) => {
    const menu = React.useContext(MenuContext);
    const item = useListItem({ label: disabled ? null : label });
    const tree = useFloatingTree();
    const isActive = item.index === menu.activeIndex;

    return (
        <MenuListItem
            {...props}
            ref={useMergeRefs([item.ref, ref])}
            type="button"
            role="menuitem"
            className="MenuItem"
            tabIndex={isActive ? 0 : -1}
            disabled={disabled}
            {...menu.getItemProps({
                onClick(event: React.MouseEvent<HTMLButtonElement>) {
                    !disabled && props.onClick?.(event);
                    tree?.events.emit("click");
                },
                onFocus(event: React.FocusEvent<HTMLButtonElement>) {
                    props.onFocus?.(event);
                    menu.setHasFocusInside(true);
                }
            })}
            style={{       //MenuItem#
                display: 'flex',
            }}
            css={{
                opacity: 1,
                background: disabled? 'lavender' : 'ghostwhite',
                ":focus,:not([disabled]):active": {          //点亮的
                    background:  'mistyrose'
                },
            }}
        >
            {children? children : label }
        </MenuListItem>
    );
};


interface MenuProps {
    //可支持搜索菜单项目的 文本。
    label?: string;
    //实际 没必要的参数
    nested?: boolean;
    children?: React.ReactNode;
    //后面安放的图标
    // contentAfter?: React.ReactNode;
    //前面安放的图标
    //  contentBefore?: React.ReactNode;
    //任意的组件， 简易按钮 图标
    icon?: React.ReactNode;
    //菜单按钮的样式 ：还是能独立配置为佳。
    divStyle?: React.CSSProperties;
    /**可以将这里没定义的参数 tight={true} 透明传递下去。
     * */
    [key: string]: any;     //任意参数传递模式！
}

/**不是叶子的节点菜单项。根部或者嵌套的树杈按钮。
 * 注意icon有没有会影响到树杈或者根部按钮菜单项的高度！icon决定了到底是string纯粹文本的，还是嵌套div组件图标按钮的。
 * 若有icon={}的就显示为图标组件。否则就按照label显示的。 两个情况的样式控制不同。
 * */
const MenuComponent =
    ({ children, label,divStyle,icon, ref, ...props }:MenuProps & React.HTMLProps<HTMLButtonElement>) => {
    const theme = useTheme();
    const [isOpen, setIsOpen] = React.useState(false);
    const [hasFocusInside, setHasFocusInside] = React.useState(false);
    const [activeIndex, setActiveIndex] = React.useState<number | null>(null);

    const elementsRef = React.useRef<Array<HTMLButtonElement | null>>([]);
    const labelsRef = React.useRef<Array<string | null>>([]);
    const parent = React.useContext(MenuContext);

    const tree = useFloatingTree();
    const nodeId = useFloatingNodeId();
    const parentId = useFloatingParentNodeId();
    const item = useListItem();
    //不是嵌套的树杈节点，就是树根root的：
    const isNested = parentId != null;

    const { floatingStyles, refs, context } = useFloating<HTMLButtonElement>({
        nodeId,
        open: isOpen,
        onOpenChange: setIsOpen,
        placement: isNested ? "right-start" : "bottom-start",
        middleware: [
            offset({ mainAxis: isNested ? 0 : 4, alignmentAxis: isNested ? -4 : 0 }),
            flip(),
            shift()
        ],
        whileElementsMounted: autoUpdate
    });

    const hover = useHover(context, {
        enabled: isNested,
        delay: { open: 75 },
        handleClose: safePolygon({ blockPointerEvents: true })
    });
    const click = useClick(context, {
        event: "mousedown",
        toggle: !isNested,
        ignoreMouse: isNested
    });
    const role = useRole(context, { role: "menu" });
    const dismiss = useDismiss(context, { bubbles: true });
    const listNavigation = useListNavigation(context, {
        listRef: elementsRef,
        activeIndex,
        nested: isNested,
        onNavigate: setActiveIndex
    });
    const typeahead = useTypeahead(context, {
        listRef: labelsRef,
        onMatch: isOpen ? setActiveIndex : undefined,
        activeIndex
    });

    const {
        getReferenceProps,
        getFloatingProps,
        getItemProps
    } = useInteractions([hover, click, role, dismiss, listNavigation, typeahead]);

    // Event emitter allows you to communicate across tree components.
    // This effect closes all menus when an item gets clicked anywhere
    // in the tree.
    React.useEffect(() => {
        if (!tree) return;

        function handleTreeClick() {
            setIsOpen(false);
        }

        function onSubMenuOpen(event: { nodeId: string; parentId: string }) {
            if (event.nodeId !== nodeId && event.parentId === parentId) {
                setIsOpen(false);
            }
        }

        tree.events.on("click", handleTreeClick);
        tree.events.on("menuopen", onSubMenuOpen);

        return () => {
            tree.events.off("click", handleTreeClick);
            tree.events.off("menuopen", onSubMenuOpen);
        };
    }, [tree, nodeId, parentId]);

    React.useEffect(() => {
        if (isOpen && tree) {
            tree.events.emit("menuopen", { parentId, nodeId });
        }
    }, [tree, isOpen, nodeId, parentId]);

    return (
        <FloatingNode id={nodeId}>
            <MenuListItem
                ref={useMergeRefs([refs.setReference, item.ref, ref])}
                tabIndex={
                    !isNested ? undefined : parent.activeIndex === item.index ? 0 : -1
                }
                role={isNested ? "menuitem" : undefined}
                data-open={isOpen ? "" : undefined}
                data-nested={isNested ? "" : undefined}
                data-focus-inside={hasFocusInside ? "" : undefined}
                className={isNested ? "MenuItem" : "RootMenu"}
                {...getReferenceProps(
                    parent.getItemProps({
                        ...props,
                        onFocus(event: React.FocusEvent<HTMLButtonElement>) {
                            props.onFocus?.(event);
                            setHasFocusInside(false);
                            parent.setHasFocusInside(true);
                        },
                        //onClick(
                    })
                )}
                css={{
                    justifyContent: 'space-between',
                    background:  'ghostwhite',
                    ":hover": {
                        //color: 'blue',
                    },
                    ...divStyle,
                }}
            >
                {icon && icon}
                {!icon && label}
                {!icon && isNested && (
                    <span aria-hidden style={{ marginLeft: 10, fontSize: 10 }}>
                        ▶
                    </span>
                ) }
            </MenuListItem>
            <MenuContext.Provider
                value={{
                    activeIndex,
                    setActiveIndex,
                    getItemProps,
                    setHasFocusInside,
                    isOpen
                }}
            >
                <FloatingList elementsRef={elementsRef} labelsRef={labelsRef}>
                    {isOpen && (
                        <FloatingPortal>
                            <FloatingFocusManager
                                context={context}
                                modal={false}
                                initialFocus={isNested ? -1 : 0}
                                returnFocus={!isNested}
                            >
                                <div
                                    ref={refs.setFloating}
                                    className="MGroup"
                                    style={{...floatingStyles,
                                        //若遭遇iframe的窗口大小，还是无法突破的，受限iframe。
                                       zIndex: theme.zIndices.menus,
                                    }}
                                    //{
                                    // onKeyDown(
                                    //    }
                                    {...getFloatingProps() }
                                >
                                    {children}
                                </div>
                            </FloatingFocusManager>
                        </FloatingPortal>
                    )}
                </FloatingList>
            </MenuContext.Provider>
        </FloatingNode>
    );
};
/*上一个版本：这个上面最近的{children}替换为如下的：
{Children.map(
            children,
            (child, index) =>
                isValidElement(child) &&
                cloneElement(
                    child,
                    getItemProps({
                        tabIndex: activeIndex === index ? 0 : -1,
                        role: "menuitem",
                        className: "MenuItem",
                        ref(node: HTMLButtonElement) {
                            listItemsRef.current[index] = node;
                        },
                        onClick(e) {
                            e.stopPropagation();
                            // console.log("Press菜单项前");
                            if(!child.props.disabled && child.props.onClick)
                                child.props.onClick(e);
                            tree?.events.emit("click");
                        },
                        // By default `focusItemOnHover` uses `mousemove` to sync focus,
                        // but when a menu closes we want this to sync it on `enter`
                        // even if the cursor didn't move. NB: Safari does not sync in
                        // this case.
                        onPointerEnter() {
                            if (allowHover) {
                                setActiveIndex(index);
                            }
                        },
                        style: {
                            //lineHeight: '4.0',纯文本不会被这个约束！只有父辈树杈按钮有icon={}的才会被影响。
                        },
                    })
                )
        )}
* */


/**根部 或 树枝分叉的父辈菜单项：
 <DdMenu>和 <DdMenuItem>不能够拆解分开！｛｝传递组件参数时要注意。
 【注意】icon={}属性可以直接头传给儿子组件<MenuComponent >的，
 <DdMenu label="菜单" divStyle={{ lineHeight: '1.0'} }>
 纯文本树杈根部按钮可以用 divStyle={{ lineHeight: '1.0'} } 来修正样式控制的，若有参数icon={}就不同了。
 例子<DdMenu label="搜索文字"  icon={
 <MenuItem variant="ghost">
 <IconMoreVertical />
 </MenuItem>
 }>  <DdMenuItem...个的  </DdMenu>
 * */
export const DdMenu =
    ({ref, ...props}: MenuProps & React.HTMLProps<HTMLButtonElement> ) => {

    const parentId = useFloatingParentNodeId();

    //支持嵌套组件的属性参数透明传递：  {...props}  方式就可以传递 icon={} css= style= 儿子组件 等。
    if (parentId == null) {
        return (
            <FloatingTree>
                <MenuComponent {...props} ref={ref}
                />
            </FloatingTree>
        );
    }

    return <MenuComponent {...props} ref={ref}
    />;
};

/* 样式例子
   css={{
       "&.active": {
           fontSize: '32px',
       },
       ":hover": {
           background: 'green',
           fontSize: '32px',
       }
   }}
* */